Skip to content

feat(differentials): interactive diagnosis detail page with honest safety snapshot#483

Merged
BigSimmo merged 19 commits into
mainfrom
claude/differentials-page-review-a3daaf
Jul 11, 2026
Merged

feat(differentials): interactive diagnosis detail page with honest safety snapshot#483
BigSimmo merged 19 commits into
mainfrom
claude/differentials-page-review-a3daaf

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • Section rows become real toggles. The six Overview rows (Why it fits, Must-not-miss, Bedside question, Immediate action, Investigations, Mimics/overlap) on /differentials/diagnoses/[slug] were static mockup rows with decorative chevrons; each section's items[] existed in the snapshot but was never rendered. They are now controlled native <details> disclosures with tone-specific bodies (numbered immediate actions sourced from the full record array, danger-tinted must-not-miss list, core-test checklist, mimic chips that link to real diagnosis pages), cleaned count badges that match the expanded content, a static fallback for the 454/1206 sections with no items, and an Expand all / Collapse all control.
  • Safety Snapshot honesty + redesign. The card hard-coded "Onset: Acute / Course: Variable / Treatable: Often" for every non-delirium diagnosis (fabricated clinical facts) and was always red. Now: curated facts map (seeded with delirium's reviewed values), derived-count facts for everything else, status-keyed theming (danger/warning/neutral), "Immediate priorities" relabelled to "Watch for" (the tags are dangers, not priorities), and a "Review must-not-miss causes" CTA that opens and scrolls to that section (reduced-motion aware).
  • Dead controls wired: Copy after review (real clipboard via a new shared copyTextToClipboard util with legacy fallback, "Copied" feedback), bookmark persisted locally (aria-pressed, survives reload), Compare buttons switch to the Compare tab, header "+" starts a new differentials search; the dead overflow button is removed.
  • Tabs: roving-tabindex arrow-key navigation (ArrowLeft/Right/Home/End), 320px-safe sizing, and shareable ?tab= deep links via history.replaceState (route stays statically generated — no useSearchParams).
  • Compare tab reworked into an honest panel: self + all related diagnoses as validated links plus "Open comparison workspace" routed to the presentation that actually contains this diagnosis (server-derived; every one of the 201 diagnoses belongs to ≥1 presentation). Fake "Clear"/"Compare selected" removed.
  • Related tab rows become links with the previously-unused per-node clinical notes; Current presentation renders clinical-hinge callouts; Source tab shows real snapshot governance (source/review status, version, export date) instead of hard-coded placeholders, upgraded live once the record hydrates.
  • Server-computed context (getDifferentialDetailContext): validated related slugs, overlap-title links, compare-presentation mapping, and governance travel as a small serializable prop so the client never bundles the 27.7k-line snapshot JSON.
  • Bug fixes in passing: map panel "Open diagnosis" always linked back to the current record (now opens the selected node); the port scanners skip Chrome/Next reserved ports (this worktree's path-derived stable port hashed to 4045, which next dev refuses — dev and Playwright servers could not boot at all), ported onto the relocated src/lib/local-server-utils.mjs; adopted fix(differentials): restore visible Safety Snapshot alert tint #468's full-opacity soft-token fix across the new status themes and all other -soft/NN washes on this page.
  • Tests: 41 new vitest cases for the pure helpers incl. catalog-wide integrity assertions, and a new Playwright describe (6 tests) covering disclosure toggling, expand-all, safety CTA, honest facts on non-curated records, keyboard/deep-link tabs, compare/related/source content, copy feedback, bookmark persistence, tap targets, and 320–1440px overflow guards.

Verification

  • npm run verify:pr-local — constituents run individually on the merged HEAD: check:runtime ✓, format:check ✓ on every file this PR touches (the repo-wide sweep locally also flags CLAUDE.md + .claude/settings.json, pre-existing CRLF working-copy artifacts untouched by this PR and green in CI on main), lint ✓, typecheck ✓, test ✓ (1,492 passed / 1 skipped), build ✓ (compiled successfully)
  • npm run verify:ui — full Chromium suite on the merged HEAD from a pristine dev server: everything passes except two tests that fail identically on pure origin/main (verified at detached f1d350cc2 with a fresh .next): ui-smoke › newer routed differential context wins over an older response (search fires 2× on load) and ui-tools › all mode home heroes share identical sizing on mobile (hero heading 25.2px vs 25.6px contract). Both are untagged, so they run only in CI's advisory lane, not the required ui-critical gate; both are pre-existing upstream regressions flagged for separate fixes. All six new differential detail-page tests pass.
  • npm run verify:release — not run (not a release/handoff claim)
  • npm run eval:retrieval:quality — N/A: no retrieval, ranking, selection, chunking, or scoring behavior changed (differentials.ts gained a read-only catalog-context helper; rankers untouched)
  • npm run eval:rag -- --limit 15 — N/A: no answer generation, synthesis prompt, or answer post-processing changes
  • npm run check:production-readiness — N/A: no clinical workflow, privacy, environment, Supabase, source-governance, or deployment behavior changed
  • npm run check:deployment-readiness — N/A: no deployment startup/hosting/rollout changes

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use — the "Clinical decision support only. Review before use." disclaimer is retained, and the copy register embeds it
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval — none touched
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy) — no env/config change
  • Service-role keys and private document access remain server-only — catalog context is computed server-side; no key handling changed
  • Demo/synthetic content remains clearly separated from real clinical sources — unchanged
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative — improved: fabricated per-diagnosis clinical facts removed in favour of derived counts, and the Source tab now surfaces the real conservative governance statuses instead of placeholders
  • Deployment classification/TGA SaMD impact was checked — no new decision-support capability added; the change renders existing snapshot content that was previously hidden and removes fabricated display values

Notes

  • Merged origin/main into the branch (no rebase); resolved conflicts in the two port scripts (upstream relocated local-server-utils.mjs to src/lib/ — reserved-port skip ported to the new location) and in the Safety Snapshot (kept the new status-themed structure while adopting fix(differentials): restore visible Safety Snapshot alert tint #468's full-opacity soft-token intent everywhere on the page).
  • Screenshots (desktop/mobile, light/dark, collapsed/expanded, non-curated record) were captured during QA.
  • Deferred follow-ups flagged for later: parser-level cleanup of noisy snapshot items, compare-basket preselect on the workflow page, surfacing saved differentials in the favourites hub, structured per-diagnosis course facts in the data model.

🤖 Generated with Claude Code

BigSimmo and others added 8 commits July 10, 2026 21:54
…clipboard util

- src/lib/differential-detail.ts: pure client-safe helpers (item cleaning,
  tone-aware visible items, honest safety facts, copy text, presentation
  grouping, tab ids) with vitest coverage
- getDifferentialDetailContext in src/lib/differentials.ts: server-side
  catalog lookups (validated related slugs, overlap title links, compare
  presentation mapping, snapshot governance) passed as a small prop so the
  client never bundles the snapshot JSON
- shared copyTextToClipboard with legacy fallback, adopted by the service
  detail page and CopyAfterReviewButton
- saved-registry storage key for locally saved differentials

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Section rows become controlled native <details> disclosures revealing the
  previously unrendered section items, with tone-specific bodies (numbered
  immediate actions, danger-tinted must-not-miss list, linked mimic chips),
  cleaned count badges, empty-section static fallback, and expand/collapse all
- Safety snapshot: status-aware theming (danger/warning/neutral instead of
  always red), honest facts (curated for delirium, derived counts elsewhere),
  "Watch for" chip relabel, and a review-must-not-miss CTA that opens and
  scrolls to the section
- Dead controls wired: Copy after review (real clipboard + Copied feedback),
  Compare buttons switch to the compare tab, bookmark persists locally with
  aria-pressed, header "+" starts a new differentials search; dead overflow
  button removed
- Compare tab reworked into an honest panel linking related diagnoses and the
  matching comparison workspace; Related rows become links with notes;
  Current presentation renders hinge callouts; Source tab shows real snapshot
  governance (live values when hydrated)
- Tabs gain roving-tabindex arrow-key navigation and ?tab= deep links via
  history.replaceState; map panel "Open diagnosis" now routes to the selected
  related node

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… Next-reserved dev ports

- New ui-tools describe block: disclosure toggling with items, expand all,
  safety CTA scroll-open, honest facts on non-curated records, keyboard tab
  navigation and ?tab= deep links, compare/related/source content, copy
  feedback, bookmark persistence, tap targets, and 320-1440px overflow guards
- Port scanners skip Chrome/Next reserved ports (e.g. 4045) so worktrees whose
  path-derived stable port lands on one can still boot dev and Playwright
  servers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rst-load waits

- Safety snapshot chips run through cleanDifferentialItem so export artifacts
  like trailing full stops do not render
- Detail-page e2e navigations share a 30s first-assertion helper (the route is
  not in the runner warm-up smoke set, so the first hit of a run can pay the
  dev-server compile)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…page-review-a3daaf

# Conflicts:
#	scripts/dev-free-port.mjs
#	scripts/local-server-utils.mjs
#	scripts/run-playwright.mjs
#	src/components/differentials/differential-detail-page.tsx
…server-utils

The origin/main merge relocated local-server-utils.mjs to src/lib/; the
reserved-port additions were applied to the working tree during conflict
resolution but missed the index, leaving the committed scripts importing a
missing isReservedDevPort export.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@supabase

supabase Bot commented Jul 11, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The pull request adds an interactive differential diagnosis detail page with server-provided context, saved state, accessible tabs, related-diagnosis navigation, and expanded tests. It also centralizes clipboard fallback behavior and excludes reserved development ports.

Changes

Differential diagnosis detail flow

Layer / File(s) Summary
Detail context and presentation helpers
src/lib/differential-detail.ts, src/lib/differentials.ts
Adds typed context, normalization, safety facts, copy formatting, presentation grouping, status labels, related-slug mapping, overlap links, and governance metadata.
Route and client context wiring
src/app/differentials/..., src/app/api/differentials/..., src/components/clinical-dashboard/..., src/components/differentials/differential-diagnosis-page-client.tsx
Adds detail context to diagnosis API responses and passes server or live context into the detail page.
Interactive detail page and diagnosis navigation
src/components/differentials/differential-detail-page.tsx, src/components/differentials/diagnosis-map-panel.tsx, src/lib/saved-registry-storage.ts
Adds controlled sections, context-aware tabs and panels, saved-state persistence, URL synchronization, keyboard navigation, and related diagnosis links.
Detail behavior validation
tests/differential-detail.test.ts, tests/differentials-route.test.ts, tests/ui-tools.spec.ts, tests/ui-smoke.spec.ts, docs/branch-review-ledger.md
Tests helper outputs, API context, responsive interactions, persistence, tab navigation, deep links, rendered content, and updated source-status behavior.

Shared clipboard handling

Layer / File(s) Summary
Clipboard utility and integrations
src/lib/copy-to-clipboard.ts, src/components/differentials/differential-presentation-actions.tsx, src/components/services/service-detail-page.tsx, tests/copy-to-clipboard.test.ts
Centralizes Clipboard API copying with a textarea fallback, uses it in detail actions, and tests success and failure paths.

Development port reservation

Layer / File(s) Summary
Reserved-port filtering
src/lib/local-server-utils.mjs, scripts/dev-free-port.mjs, scripts/run-playwright.mjs
Defines reserved development ports, skips them during stable-port selection and free-port discovery, and wraps within the configured range when needed.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant DiagnosisRoute
  participant DetailContext
  participant DifferentialDetailPage
  participant LocalStorage
  Browser->>DiagnosisRoute: Request diagnosis route
  DiagnosisRoute->>DetailContext: Build detailContext for record
  DiagnosisRoute->>DifferentialDetailPage: Render record and context
  DifferentialDetailPage->>LocalStorage: Read and write saved diagnosis state
  Browser->>DifferentialDetailPage: Change tab, expand section, copy, or compare
  DifferentialDetailPage-->>Browser: Render context-aware detail view
Loading
🚥 Pre-merge checks | ✅ 10 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.47% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (10 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: an interactive differential diagnosis detail page with improved safety snapshot handling.
Description check ✅ Passed The description follows the template with Summary, Verification, and Clinical Governance sections and is mostly complete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Generated And Sensitive Files ✅ Passed Changed files are only docs/scripts/src/tests; scans found no real secrets or artifact paths, and the test token is a dummy valid-token.
Verification Claims ✅ Passed The PR/ledger name exact checks and outcomes (npm run verify:pr-local, npm run verify:ui, Focused differentials route/catalog Vitest (26/26)), so the verification wording is specific enough.
Risky Git Or Deployment Actions ✅ Passed Scanned changed scripts/docs/tests; found no force-push, hard reset, destructive clean, branch deletion, rebasing, or deployment-without-confirmation guidance.
Supabase Project And Schema Safety ✅ Passed No Supabase project/env/migration files changed; the only Supabase code is read-only query/client wiring, with no schema/RLS/policy ops.
Runtime And Package Manager Integrity ✅ Passed PR only changes app scripts/tests; no package-manager files or flags changed. Repo still pins npm@11.17.0, Node 24.x, engine-strict, and keeps package-lock.json.
Api Route Failure Handling ✅ Passed The API route has deterministic handling: invalid queries, auth failures, missing records, rate limits, and thrown env/DB errors all map to explicit responses.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/differentials-page-review-a3daaf
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/differentials-page-review-a3daaf

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7c94fae052

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/copy-to-clipboard.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
scripts/run-playwright.mjs (1)

71-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider consolidating duplicated findFreePort logic.

findFreePort here is nearly identical to the one in scripts/dev-free-port.mjs (differing only in the loop bound: projectPortEnd vs maxPort). Now that both share isReservedDevPort/stableProjectPort from local-server-utils.mjs, extracting a shared findFreePort(startPort, endPort) helper into that module would remove the remaining duplication and prevent future drift (e.g., if the reserved-port skip logic needs to change again).

♻️ Suggested shared helper (illustrative)
// src/lib/local-server-utils.mjs
export async function findFreePort(startPort, endPort, canListen) {
  for (let port = startPort; port <= endPort; port += 1) {
    if (isReservedDevPort(port)) continue;
    if (await canListen(port)) return port;
  }
  throw new Error(`No free port found from ${startPort} to ${endPort}.`);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/run-playwright.mjs` around lines 71 - 77, Consolidate the duplicated
findFreePort logic from scripts/run-playwright.mjs and scripts/dev-free-port.mjs
into a shared findFreePort(startPort, endPort, canListen) helper in
local-server-utils.mjs. Preserve reserved-port skipping and inclusive bounds,
update both callers to pass their respective end ports and canListen functions,
and retain clear no-free-port error handling.
src/lib/differential-detail.ts (1)

138-157: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Investigations list is unbounded in the clipboard register, unlike actions.

actions is capped to slice(0, 6) (Line 145) but investigations (Lines 150-154) has no cap. A record with many investigations produces an unbounded "Copy after review" clipboard payload, inconsistent with the actions cap.

💡 Proposed fix
-  const investigations = record.investigations.map(cleanDifferentialItem).filter(Boolean);
+  const investigations = record.investigations.map(cleanDifferentialItem).filter(Boolean).slice(0, 6);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/differential-detail.ts` around lines 138 - 157, Cap the
investigations included by formatDifferentialCopyText to the same six-item limit
as immediate actions. Update the investigations mapping/filtering chain to apply
slice(0, 6) before rendering the “Investigations” section.
src/lib/differentials.ts (1)

124-159: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Redundant snapshot load and uncached catalog Set inside getDifferentialDetailContext.

Two inefficiencies compound in this function, called once per diagnosis page render/build:

  • Line 125 rebuilds catalogSlugs as a fresh Set on every call, unlike the memoized diagnosisTitleSlugMap() pattern used just above it.
  • Line 140 calls differentialPresentations() (which internally loads the snapshot via catalog()), then Line 144 calls loadDifferentialSnapshot() again directly — reloading/reparsing the same snapshot twice per call. Since catalog() is literally return loadDifferentialSnapshot();, this second call is redundant and can reuse the same snapshot object.

Across many statically generated diagnosis pages (generateStaticParams), this adds up to unnecessary repeated I/O/parsing during build.

♻️ Proposed fix
   const presentation =
-    differentialPresentations().find((workflow) =>
+    catalog().presentations.find((workflow) =>
       workflow.candidates.some((candidate) => candidate.slug === record.slug),
     ) ?? null;
 
-  const snapshot = loadDifferentialSnapshot();
+  const snapshot = catalog();
   const governance = deriveGovernanceFromSnapshot(snapshot);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/differentials.ts` around lines 124 - 159, Optimize
getDifferentialDetailContext by reusing memoized data: introduce a module-level
or memoized catalog slug Set instead of rebuilding catalogSlugs on every call,
and load the differential snapshot once before deriving presentation and
governance. Reuse that snapshot for differentialPresentations or add an
equivalent snapshot-aware path so the function does not trigger the
catalog-backed load and then call loadDifferentialSnapshot again.
src/components/differentials/differential-detail-page.tsx (2)

447-501: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated related-node row markup between RelatedDiagnoses and ComparePanel.

Both components repeat the same title/note + likelihood-badge + conditional Link-vs-div wrapper structure (lines 456-497 and 598-630) with only cosmetic class differences. Extracting a small shared RelatedNodeRow helper (taking node, known, and a variant for styling) would remove ~50 lines of near-identical JSX and keep future tone/badge changes in one place.

Also applies to: 597-631

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/differentials/differential-detail-page.tsx` around lines 447 -
501, Extract the duplicated related-node row JSX from RelatedDiagnoses and
ComparePanel into a shared RelatedNodeRow helper that accepts the node,
known-state, and styling variant. Preserve the existing title, note, likelihood
badge, conditional Link-versus-div behavior, test id, href, and cosmetic class
differences, then replace both inline implementations with the helper.

812-844: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

aria-controls on inactive tabs points to an unmounted panel id.

Every tab button sets aria-controls={differential-panel-${tab.id}}, but only one <div role="tabpanel" id={differential-panel-${activeTab}}> is ever rendered (line 1002). For the four inactive tabs, aria-controls references an id that doesn't exist in the DOM, which is a real ARIA authoring-practices violation for tabs (each tab's aria-controls should resolve to an existing panel element).

🔧 Proposed fix
-            aria-controls={`differential-panel-${tab.id}`}
+            aria-controls={isActive ? `differential-panel-${tab.id}` : undefined}

Also applies to: 853-865

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/differentials/differential-detail-page.tsx` around lines 812 -
844, Fix the tab/panel ARIA relationship in the Tabs component and its tab
button rendering: ensure every tab’s aria-controls references an existing panel
element, either by rendering panels for all tabs or by removing aria-controls
from inactive tabs and retaining it only for the active tab. Keep the panel IDs
consistent with the tab IDs and update related aria attributes as needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/differentials/differential-diagnosis-page-client.tsx`:
- Around line 17-25: Recompute detailContext from the rendered resolvedRecord
instead of retaining the SSR snapshot context. Update the differential diagnosis
page component and its supporting helper or type definitions as needed so
overlap, related, compare, and source panels derive their context from
resolvedRecord for both fallback and fetched records.

In `@src/lib/copy-to-clipboard.ts`:
- Around line 22-27: Ensure the fallback copy path throws when
document.execCommand is unavailable or rejects the copy. In the try block of the
clipboard helper, explicitly verify that execCommand is a function before
invoking it, and throw an appropriate error when it is missing or returns false,
so callers such as CopyAfterReviewButton can detect failure.

---

Nitpick comments:
In `@scripts/run-playwright.mjs`:
- Around line 71-77: Consolidate the duplicated findFreePort logic from
scripts/run-playwright.mjs and scripts/dev-free-port.mjs into a shared
findFreePort(startPort, endPort, canListen) helper in local-server-utils.mjs.
Preserve reserved-port skipping and inclusive bounds, update both callers to
pass their respective end ports and canListen functions, and retain clear
no-free-port error handling.

In `@src/components/differentials/differential-detail-page.tsx`:
- Around line 447-501: Extract the duplicated related-node row JSX from
RelatedDiagnoses and ComparePanel into a shared RelatedNodeRow helper that
accepts the node, known-state, and styling variant. Preserve the existing title,
note, likelihood badge, conditional Link-versus-div behavior, test id, href, and
cosmetic class differences, then replace both inline implementations with the
helper.
- Around line 812-844: Fix the tab/panel ARIA relationship in the Tabs component
and its tab button rendering: ensure every tab’s aria-controls references an
existing panel element, either by rendering panels for all tabs or by removing
aria-controls from inactive tabs and retaining it only for the active tab. Keep
the panel IDs consistent with the tab IDs and update related aria attributes as
needed.

In `@src/lib/differential-detail.ts`:
- Around line 138-157: Cap the investigations included by
formatDifferentialCopyText to the same six-item limit as immediate actions.
Update the investigations mapping/filtering chain to apply slice(0, 6) before
rendering the “Investigations” section.

In `@src/lib/differentials.ts`:
- Around line 124-159: Optimize getDifferentialDetailContext by reusing memoized
data: introduce a module-level or memoized catalog slug Set instead of
rebuilding catalogSlugs on every call, and load the differential snapshot once
before deriving presentation and governance. Reuse that snapshot for
differentialPresentations or add an equivalent snapshot-aware path so the
function does not trigger the catalog-backed load and then call
loadDifferentialSnapshot again.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a87c7bc-76e1-41f7-b829-43905fa57067

📥 Commits

Reviewing files that changed from the base of the PR and between dd7012c and 7c94fae.

📒 Files selected for processing (15)
  • scripts/dev-free-port.mjs
  • scripts/run-playwright.mjs
  • src/app/differentials/diagnoses/[slug]/page.tsx
  • src/components/differentials/diagnosis-map-panel.tsx
  • src/components/differentials/differential-detail-page.tsx
  • src/components/differentials/differential-diagnosis-page-client.tsx
  • src/components/differentials/differential-presentation-actions.tsx
  • src/components/services/service-detail-page.tsx
  • src/lib/copy-to-clipboard.ts
  • src/lib/differential-detail.ts
  • src/lib/differentials.ts
  • src/lib/local-server-utils.mjs
  • src/lib/saved-registry-storage.ts
  • tests/differential-detail.test.ts
  • tests/ui-tools.spec.ts

Comment thread src/components/differentials/differential-diagnosis-page-client.tsx Outdated
Comment thread src/lib/copy-to-clipboard.ts
document.execCommand?.("copy") returns undefined when the API is absent, so
the helper resolved successfully and CopyAfterReviewButton reported "Copied"
without copying. Only an explicit true now counts as success; unit tests
cover the clipboard path, the fallback, and both failure modes.

Addresses the Codex P2 and CodeRabbit findings on PR #483.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 11, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 947e836adb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/components/differentials/differential-diagnosis-page-client.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53faf4801a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/components/differentials/differential-detail-page.tsx Outdated
…ave on mobile

- /api/differentials/[slug] now returns a server-computed detailContext for
  every diagnosis payload (snapshot and owner-row branches), and the client
  prefers it once hydrated so link validation, compare target, and source info
  always match the record being rendered; SSR context stays as fallback
- The mobile action strip gains the bookmark toggle (TopActions is lg-only, so
  small viewports previously had no save control); covered by the mobile e2e
- Route test asserts the demo-mode payload carries the delirium context

Addresses the Codex P2 review findings on PR #483.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36cca1bf7c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/app/api/differentials/[slug]/route.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40342dab08

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/differentials.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 061c888026

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/differential-detail.ts Outdated
# Conflicts:
#	docs/branch-review-ledger.md
@BigSimmo
BigSimmo enabled auto-merge (squash) July 11, 2026 09:30

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c32a24b64

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/dev-free-port.mjs
Comment thread src/components/differentials/differential-detail-page.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/differentials.ts (1)

121-152: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Honor the catalog override in routability checks

catalog.records / catalog.presentations are used for lookup, but knownRelatedSlugs, overlapLinks, and comparePresentation still gate against the module-level catalog only. In the owner-row API path that passes ownerRecords / ownerPresentations, any owner-specific related diagnosis or presentation not present in the bundled snapshot is dropped from the detail context.

Use the override entries when building the routable slug/id sets, or the catalog parameter only works for title matching and not for link generation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/differentials.ts` around lines 121 - 152, The routability sets in
getDifferentialDetailContext currently use the module-level differentialRecords
and differentialPresentations instead of the resolved catalogRecords and
catalogPresentations. Build routableDiagnosisSlugs and routablePresentationSlugs
from the catalog override values so knownRelatedSlugs, overlapLinks, and
presentation/comparePresentation retain owner-specific entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/lib/differentials.ts`:
- Around line 121-152: The routability sets in getDifferentialDetailContext
currently use the module-level differentialRecords and differentialPresentations
instead of the resolved catalogRecords and catalogPresentations. Build
routableDiagnosisSlugs and routablePresentationSlugs from the catalog override
values so knownRelatedSlugs, overlapLinks, and presentation/comparePresentation
retain owner-specific entries.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0cf95da8-8ff8-4bbe-b7e0-b892be43aeb1

📥 Commits

Reviewing files that changed from the base of the PR and between 40342da and 0c32a24.

📒 Files selected for processing (5)
  • docs/branch-review-ledger.md
  • src/lib/differentials.ts
  • tests/differentials-route.test.ts
  • tests/ui-smoke.spec.ts
  • tests/ui-tools.spec.ts
✅ Files skipped from review due to trivial changes (2)
  • docs/branch-review-ledger.md
  • tests/ui-smoke.spec.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/differentials-route.test.ts
  • tests/ui-tools.spec.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 11, 2026
@BigSimmo
BigSimmo merged commit f334b1a into main Jul 11, 2026
15 checks passed
@BigSimmo
BigSimmo deleted the claude/differentials-page-review-a3daaf branch July 11, 2026 13:41
BigSimmo added a commit that referenced this pull request Jul 11, 2026
…n taps

Rebuilt on top of #483's redesigned interactive detail page (which already fixed
the wrapping action labels and tab-label wrap). Remaining polish:

- Add a .focus-ring-tab class (inset rounded ring: outline-offset -4px, no halo)
  so keyboard focus no longer draws a full-height box that collides with the
  tablist border; inactive tabs get a transparent bottom border to remove the
  2px layout jump on tab switch.
- Keep #483's roving-tabindex keyboard nav, nowrap and flex-1 layout intact.
- shrink-0 on the desktop action cluster so the Save icon button stops being
  crushed below 44px at ~1024px; nowrap on the Compare button.
- Bump the two overlap-diagnosis chips to the 44px tap standard.
- Regression test: detail Save action >=44px at 1024px, Overview tab single-line
  with no page overflow at 320px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BigSimmo added a commit that referenced this pull request Jul 11, 2026
…n taps

Rebuilt on top of #483's redesigned interactive detail page (which already fixed
the wrapping action labels and tab-label wrap). Remaining polish:

- Add a .focus-ring-tab class (inset rounded ring: outline-offset -4px, no halo)
  so keyboard focus no longer draws a full-height box that collides with the
  tablist border; inactive tabs get a transparent bottom border to remove the
  2px layout jump on tab switch.
- Keep #483's roving-tabindex keyboard nav, nowrap and flex-1 layout intact.
- shrink-0 on the desktop action cluster so the Save icon button stops being
  crushed below 44px at ~1024px; nowrap on the Compare button.
- Bump the two overlap-diagnosis chips to the 44px tap standard.
- Regression test: detail Save action >=44px at 1024px, Overview tab single-line
  with no page overflow at 320px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BigSimmo added a commit that referenced this pull request Jul 11, 2026
* fix(differentials): shape the detail tab focus ring and firm up action taps

Rebuilt on top of #483's redesigned interactive detail page (which already fixed
the wrapping action labels and tab-label wrap). Remaining polish:

- Add a .focus-ring-tab class (inset rounded ring: outline-offset -4px, no halo)
  so keyboard focus no longer draws a full-height box that collides with the
  tablist border; inactive tabs get a transparent bottom border to remove the
  2px layout jump on tab switch.
- Keep #483's roving-tabindex keyboard nav, nowrap and flex-1 layout intact.
- shrink-0 on the desktop action cluster so the Save icon button stops being
  crushed below 44px at ~1024px; nowrap on the Compare button.
- Bump the two overlap-diagnosis chips to the 44px tap standard.
- Regression test: detail Save action >=44px at 1024px, Overview tab single-line
  with no page overflow at 320px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): click-ring artifact and sub-44px tap targets across production routes

Still-present defects confirmed by a fresh sweep of current main (15 routes x 7
widths); none were addressed by recent main changes:

- DocumentTagCloud: drop the focus: ring classes that painted a ring on mouse
  click; the global :focus-visible rule keeps the keyboard ring.
- applications launcher category filters: mobile 28px / desktop 36px -> 44px,
  with whitespace-nowrap.
- service detail Copy row buttons: 40px -> 44px.
- favourites command-library More-actions buttons and library sidebar rows and
  compact toggle: 36px -> 44px.

Deliberately left main's intentional lg:min-h-9 desktop density on the mode-home
quick-link pills (AA-compliant for pointer).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(medication): use per-prompt action descriptions on the prescribing home

The MedicationHome actions hardcoded description: "Prescribing-focused search."
for every prompt, ignoring the authored `medicationPrompts[].description` values
("Check renal dosing…", "Review opioid-use…", "Check maximum dose…") that the
prescribing-home responsive test asserts. Restore `description: prompt.description`
so each card shows its specific guidance and the advisory UI regression passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* 📝 Add docstrings to `claude/site-formatting-polish-b91374` (#503)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants